package com.nickstumpos.android.easyVolume;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;

import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.TextView;

import com.nickstumpos.android.easyVolume.constants.EVCONSTANTS;
import com.nickstumpos.android.easyVolume.events.Dto.EventTriggerDto;
import com.nickstumpos.android.easyVolume.events.service.EventTriggerService;
import com.nickstumpos.android.easyVolume.events.util.EventServiceUtil;
import com.nickstumpos.android.easyVolume.manager.ScreenManager;
import com.nickstumpos.android.easyVolume.preset.presentation.PresetScreen;
import com.nickstumpos.android.easyVolume.streams.AbstractVolumeScreen;
import com.nickstumpos.android.easyVolume.streams.presentation.types.Screen;

public class EasyVolumeProto extends Activity implements View.OnTouchListener {

	private ViewGroup													group;
	private Screen														current;
	private boolean														isDown;
	private float															downX;
	private float															downY;
	private ScreenManager											manager					= null;
	private SharedPreferences									settings;
	private Map<Screen, AbstractVolumeScreen>	views						= new HashMap<Screen, AbstractVolumeScreen>();
	private boolean														showingPresets	= false;
	private PresetScreen											presetScreen		= null;

	private void assignViews() {
		this.views.put(Screen.MASTER, (AbstractVolumeScreen) this
				.findViewById(R.id.Master));
		this.views.put(Screen.SYSTEM, (AbstractVolumeScreen) this
				.findViewById(R.id.System));
		this.views.put(Screen.NOTIFICATION, (AbstractVolumeScreen) this
				.findViewById(R.id.Notification));
		this.views.put(Screen.VOICE, (AbstractVolumeScreen) this
				.findViewById(R.id.Voice));
		this.views.put(Screen.ALARM, (AbstractVolumeScreen) this
				.findViewById(R.id.Alarm));
		this.views.put(Screen.MEDIA, (AbstractVolumeScreen) this
				.findViewById(R.id.Media));
		this.views.put(Screen.RINGER, (AbstractVolumeScreen) this
				.findViewById(R.id.Ringer));
		this.presetScreen = (PresetScreen) this.findViewById(R.id.PresetsLayout);
	}

	private void eventService() {

		String old = this.settings.getString(EVCONSTANTS.EVENT_SCHED_DTO, null);
		if (old != null) {
			final byte[] buf = Base64.decodeBase64(old.getBytes());
			ObjectInputStream in = null;
			try {
				in = new ObjectInputStream(new ByteArrayInputStream(buf));
				EventTriggerDto eventTriggerDto = (EventTriggerDto) in.readObject();
				if (eventTriggerDto != null) {
					if (EventServiceUtil.shouldStartService(eventTriggerDto)) {
						this.startService(new Intent(this, EventTriggerService.class));
					}
				}
			} catch (final IOException e) {
			} catch (final ClassNotFoundException e) {
			}
		}
	}

	private boolean moveLeft() {
		boolean ret = true;
		switch (this.current) {
			case ALARM:
				this.views.get(Screen.VOICE).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.ALARM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.VOICE);
				ret = true;
				break;
			case MASTER:
				this.views.get(Screen.RINGER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.MASTER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.RINGER);
				ret = true;
				break;
			case MEDIA:
				this.views.get(Screen.ALARM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.MEDIA).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.ALARM);
				ret = true;
				break;
			case NOTIFICATION:
				this.views.get(Screen.SYSTEM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.NOTIFICATION).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.SYSTEM);
				ret = true;
				break;
			case RINGER:
				this.views.get(Screen.MEDIA).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.RINGER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.MEDIA);
				ret = true;
				break;
			case SYSTEM:
				this.views.get(Screen.SYSTEM).startAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_half_out));
				ret = true;
				this.setCurrentView(Screen.SYSTEM);
				break;
			case VOICE:
				this.views.get(Screen.NOTIFICATION).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_in));
				this.views.get(Screen.VOICE).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_left_out));
				this.setCurrentView(Screen.NOTIFICATION);
				ret = true;
				break;
			default:
				ret = false;
				break;
		}
		return ret;
	}

	private boolean moveRight() {
		boolean ret = false;
		switch (this.current) {
			case ALARM:
				this.views.get(Screen.MEDIA).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.ALARM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.MEDIA);
				ret = true;
				break;
			case MASTER:
				this.views.get(Screen.MASTER).startAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_half_out));
				this.setCurrentView(Screen.MASTER);
				ret = true;
				break;
			case MEDIA:
				this.views.get(Screen.RINGER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.MEDIA).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.RINGER);
				ret = true;
				break;
			case NOTIFICATION:
				this.views.get(Screen.VOICE).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.NOTIFICATION).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.VOICE);
				ret = true;
				break;
			case RINGER:
				this.views.get(Screen.MASTER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.RINGER).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.MASTER);
				ret = true;
				break;
			case SYSTEM:
				this.views.get(Screen.NOTIFICATION).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.SYSTEM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.NOTIFICATION);
				ret = true;
				break;
			case VOICE:
				this.views.get(Screen.ALARM).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_in));
				this.views.get(Screen.VOICE).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_right_out));
				this.setCurrentView(Screen.ALARM);
				ret = true;
				break;
			default:
				ret = false;
				break;

		}
		return ret;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (requestCode == EVCONSTANTS.EDIT_TEXT_RETURN) {
			this.presetScreen.refreshButtonTitles();
		} else if (requestCode == EVCONSTANTS.SCHED_RETURN) {
			this.reset();
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.contentwrapper);
		if (this.manager == null) {
			this.group = (ViewGroup) this.findViewById(R.id.container);
			this.manager = new ScreenManager(this);
			this.setupViews(savedInstanceState);
			this.settings = this.getSharedPreferences(EVCONSTANTS.PREFS_NAME, 0);
			this.eventService();
		}
	}

	/* Creates the menu items */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		menu.add(0, EVCONSTANTS.HELP_MENU, 0, "Help");
		menu.add(0, EVCONSTANTS.EXIT_MENU, 0, "Exit");
		return true;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		boolean ret = false;
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (this.showingPresets) {
				this.togglePresetVisible(true);
				ret = true;
			} else if (this.current.equals(Screen.MASTER)) {
				this.terminate();
				ret = true;
			} else {
				this.moveRight();
				ret = true;
			}
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
			if (!this.showingPresets) {
				this.views.get(this.current).DecreaseVolumeLevel();
			}
			ret = true;
		} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
			if (!this.showingPresets) {
				this.views.get(this.current).IncreaseVolumeLevel();
			}
			ret = true;
		} else {
			ret = super.onKeyDown(keyCode, event);
		}
		return ret;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
			case EVCONSTANTS.HELP_MENU:
				this.showHelp();
				return true;
			case EVCONSTANTS.EXIT_MENU:
				this.terminate();
				return true;
		}
		return false;
	}

	@Override
	public void onPause() {
		super.onPause();
	}

	@Override
	public void onResume() {
		super.onResume();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState
				.putString(EVCONSTANTS.CURRENT_SCREEN_SAVE, this.current.toString());
		outState.putBoolean(EVCONSTANTS.SHOWING_PRESETS_SAVE, this.showingPresets);
	}

	@Override
	public void onStart() {
		super.onStart();
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		boolean ret = false;
		if (!this.showingPresets) {
			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				ret = true;
				this.isDown = true;
				this.downX = event.getX();
				this.downY = event.getY();
			} else if (event.getAction() == MotionEvent.ACTION_UP && this.isDown) {
				this.isDown = false;
				float changeX = Math.abs(this.downX - event.getX());
				float changeY = Math.abs(this.downY - event.getY());
				if (changeY > changeX && this.downY < event.getY()) {
					this.togglePresetVisible(true);
				} else {
					if (this.downX > event.getX()) {
						ret = this.moveLeft();
					} else if (this.downX < event.getX()) {
						ret = this.moveRight();
					}
				}
			}
		} else {
			if (this.downY < event.getY()) {
				ret = this.togglePresetVisible(true);
			}
		}
		return ret;
	}

	private void registerlisteners() {
		for (Screen screen : Screen.values()) {
			this.views.get(screen).setOnTouchListener(this);
		}
		this.presetScreen.setOnTouchListener(this);
	}

	private void registerScreens() {
		for (Screen screen : Screen.values()) {
			this.manager.registerScreen(this.views.get(screen), screen);
		}
	}

	private void reset() {
		Bundle save = new Bundle();
		this.onSaveInstanceState(save);
		this.current = null;
		this.manager = null;
		this.views.clear();
		this.showingPresets = false;
		this.presetScreen = null;
		this.onCreate(save);
	}

	private void setCurrentView(Screen cur) {
		for (Screen screen : Screen.values()) {
			View view = this.views.get(screen);
			if (screen.equals(cur)) {
				this.current = screen;
				view.setVisibility(View.VISIBLE);
				view.requestFocus();
			} else if (view.getVisibility() != View.GONE) {
				view.setVisibility(View.GONE);
			}
		}
	}

	private void setupViews(Bundle saved) {
		this.group.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
		this.assignViews();
		this.registerScreens();
		this.registerlisteners();
		this.presetScreen.setVisibility(View.GONE);
		if (saved != null && saved.containsKey(EVCONSTANTS.CURRENT_SCREEN_SAVE)) {
			this.setCurrentView(Screen.valueOf(saved
					.getString(EVCONSTANTS.CURRENT_SCREEN_SAVE)));
			if (saved.containsKey(EVCONSTANTS.SHOWING_PRESETS_SAVE)) {
				if (saved.getBoolean(EVCONSTANTS.SHOWING_PRESETS_SAVE)) {
					this.togglePresetVisible(false);
				}
			}
		} else {
			this.setCurrentView(Screen.MASTER);
		}
		this.manager.restoreSettings();
	}

	private void showHelp() {
		Dialog helpMenu = new Dialog(this);
		TextView tv = new TextView(this);
		tv
				.setText("\tThis app allows the user to control individual stream volumes, as well as multiple streams simultaneously. \n\t"
						+ "The current shown stream volume may be adjusted by touching the level on the screen or by pressing the volume keys. "
						+ "\n\tThe first screen is the master controller which sets all of the slave streams to the selected volume.\n\t"
						+ "Scroll to the sides to see the individual streams. Check the box to set a stream as controlled by the master. Uncheck"
						+ " to set individual volumes. \n\tAt anytime push down the screen to see the presets menu.  A long click will set the presets or "
						+ "allow a name change. \n\t The play button on each screen will play the default ringtone on the current stream. \n\tVibrate control is "
						+ "also available where appplicable.\n");
		helpMenu.setTitle("Help");
		helpMenu.setContentView(tv);
		helpMenu.show();
	}

	public void terminate() {
		super.onDestroy();
		this.finish();
	}

	private boolean togglePresetVisible(boolean animate) {
		boolean ret;
		if (this.showingPresets) {
			if (animate) {
				this.presetScreen.setAnimation(AnimationUtils.loadAnimation(this,
						R.anim.push_up_out));
				this.views.get(this.current).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_up_in));
			}
			this.views.get(this.current).setVisibility(View.VISIBLE);
			this.presetScreen.setVisibility(View.GONE);
			this.showingPresets = false;
			ret = true;
		} else {
			if (animate) {
				this.presetScreen.setAnimation(AnimationUtils.loadAnimation(this,
						R.anim.push_down_in));
				this.views.get(this.current).setAnimation(
						AnimationUtils.loadAnimation(this, R.anim.push_down_out));
			}
			this.showingPresets = true;
			ret = true;
			this.views.get(this.current).setVisibility(View.GONE);
			this.presetScreen.setVisibility(View.VISIBLE);
		}
		return ret;
	}

}